import tkinter as tk
import os
import json
import smtplib
import threading
import tempfile
import time
import py7zr
import multivolumefile
import base64
import windnd
from tkinter import filedialog, ttk, messagebox
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email import encoders
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC

class sendtoABCmail():
    
    def __init__(self):
        self.root = tk.Tk()
        self.root.withdraw()
        self.root.title("SendToEmail")
        self.root.geometry("300x385")
        self.root.resizable(False, False)
        
        try:
            root_dir = os.path.dirname(os.path.abspath(__file__))
            icon_path = os.path.join(root_dir, 'logo.ico')
            
            if os.path.exists(icon_path):
                self.root.iconbitmap(icon_path)
        except Exception as e:
            print(f"设置对话框图标失败: {e}")
            pass

        self.selected_files = []  # 改为列表以保持一致性

        self.notebook = ttk.Notebook(self.root)
        self.notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        self.create_send_tab()
        self.create_settings_tab()

        self.create_status_bar()

        self.load_settings()
            
        self.center_window()
        self.root.deiconify()

        windnd.hook_dropfiles(self.root, func=self.on_drop, force_unicode=True)


    def create_send_tab(self):
        """创建发送邮件标签页"""
        self.send_frame = ttk.Frame(self.notebook)
        self.notebook.add(self.send_frame, text="发送邮件")
        
        # 收件箱框架
        input_frame = tk.LabelFrame(self.send_frame, padx=10, pady=10)
        input_frame.pack(fill="x", padx=10, pady=10)

        # 接收邮箱输入框
        email_frame = tk.Frame(input_frame)
        email_frame.pack(padx=5, fill="x")
        tk.Label(email_frame, text="接收邮箱:").pack(side="left")
        self.email_entry = tk.Entry(email_frame, width=30)
        self.email_entry.pack(side="right", expand=True, fill="x")
        
        # 分卷大小输入框
        volume_frame = tk.Frame(input_frame)
        volume_frame.pack(padx=5, pady=(5,0),fill="x")
        tk.Label(volume_frame, text="分卷大小:").pack(side="left")
        self.volume_size_entry = tk.Entry(volume_frame, width=20)
        self.volume_size_entry.pack(side="left")
        self.volume_size_entry.insert(0, "20")  # 设置默认值为20
        tk.Label(volume_frame, text="MB").pack(side="right")
        
        # 解压密码输入框
        password_frame = tk.Frame(input_frame)
        password_frame.pack(pady=(5,0), padx=5, fill="x")
        tk.Label(password_frame, text="解压密码:").pack(side="left")
        self.extract_password_entry = tk.Entry(password_frame, width=20, show="*")
        self.extract_password_entry.pack(side="left")
        self.password_visible = False  # 跟踪密码是否可见
        self.toggle_password_button = tk.Button(password_frame, text="👁", command=self.toggle_password_visibility)
        self.toggle_password_button.pack(side="right")

        # 附件框架
        file_frame = tk.LabelFrame(self.send_frame, padx=10, pady=10)
        file_frame.pack(fill="x", padx=10, pady=(0,10))

        # 附件选择按钮
        tk.Button(file_frame, text="选择附件", command=self.select_files).pack(side="top", expand=True, fill="x", pady=(0,10))
        
        # 文件信息展示框
        file_list_frame = tk.Frame(file_frame)
        file_list_frame.pack(fill="both", expand=True, pady=(0,5))
        
        # 创建滚动条
        scrollbar = tk.Scrollbar(file_list_frame)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
        # 创建文本框用于显示文件信息
        self.file_info_text = tk.Text(file_list_frame, height=5, yscrollcommand=scrollbar.set)
        self.file_info_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar.config(command=self.file_info_text.yview)

        # 初始化为空白，等待用户选择文件后填充
        self.file_info_text.insert(tk.END, "请拖放文件到此窗口或使用上方的'选择附件'按钮来选择文件\n")
        self.file_info_text.config(state=tk.DISABLED)
        
        # 发送按钮
        button_frame = tk.Frame(self.send_frame)
        button_frame.pack(pady=(0,5), padx=10, fill="x")
        self.send_button = tk.Button(button_frame, text="发送", command=self.send_email)
        self.send_button.pack(side="left", expand=True, fill="x")
        

    def create_settings_tab(self):
        """创建设置标签页"""
        self.settings_frame = ttk.Frame(self.notebook)
        self.notebook.add(self.settings_frame, text="设置")
        
        # 主密码设置框架
        master_password_frame = tk.LabelFrame(self.settings_frame, padx=10, pady=5)
        master_password_frame.pack(fill="x", padx=10, pady=5)
        
        # 主密码输入框
        master_password_input_frame = tk.Frame(master_password_frame)
        master_password_input_frame.pack(fill="x", pady=5)
        tk.Label(master_password_input_frame, text="主密码:", width=11, anchor="w").pack(side="left")
        self.master_password_entry = tk.Entry(master_password_input_frame, show="*")
        self.master_password_entry.pack(side="left", expand=True, fill="x")
        
        # 设置主密码按钮
        set_master_password_button_frame = tk.Frame(master_password_frame)
        set_master_password_button_frame.pack(fill="x")
        tk.Button(set_master_password_button_frame, text="设置主密码", command=self.set_master_password).pack(side="left", expand=True, fill="x")
        
        # SMTP设置框架
        smtp_frame = tk.LabelFrame(self.settings_frame, padx=10, pady=(5))
        smtp_frame.pack(fill="x", padx=10, pady=5)
        
        # SMTP服务器
        server_frame = tk.Frame(smtp_frame)
        server_frame.pack(fill="x", pady=5)
        tk.Label(server_frame, text="SMTP服务器:", width=11, anchor="w").pack(side="left")
        self.smtp_server_entry = tk.Entry(server_frame)
        self.smtp_server_entry.pack(side="left", expand=True, fill="x")
        self.smtp_server_entry.insert(0, "smtp.example.com")
        
        # SMTP端口
        port_frame = tk.Frame(smtp_frame)
        port_frame.pack(fill="x", pady=(0,5))
        tk.Label(port_frame, text="端口:", width=11, anchor="w").pack(side="left")
        self.smtp_port_entry = tk.Entry(port_frame)
        self.smtp_port_entry.pack(side="left",expand=True, fill="x")
        self.smtp_port_entry.insert(0, "587")
        
        # 发送邮箱
        sender_frame = tk.Frame(smtp_frame)
        sender_frame.pack(fill="x", pady=(0,5))
        tk.Label(sender_frame, text="发送邮箱:", width=11, anchor="w").pack(side="left")
        self.sender_email_entry = tk.Entry(sender_frame)
        self.sender_email_entry.pack(side="left", expand=True, fill="x")
        
        # 密码
        password_frame = tk.Frame(smtp_frame)
        password_frame.pack(fill="x", pady=(0,5))
        tk.Label(password_frame, text="密码/授权码:", width=11, anchor="w").pack(side="left")
        self.password_entry = tk.Entry(password_frame, width=18, show="*")
        self.password_entry.pack(side="left")
        self.smtp_password_visible = False  # 跟踪SMTP密码是否可见
        self.toggle_smtp_password_button = tk.Button(password_frame, text="👁", command=self.toggle_smtp_password_visibility)
        self.toggle_smtp_password_button.pack(side="right")

        # 测试按钮
        test_button_frame = tk.Frame(smtp_frame)
        test_button_frame.pack(fill="x")
        tk.Button(test_button_frame, text="测试", command=self.test_smtp_connection).pack(side="left", expand=True, fill="x")       
        
        # 保存按钮
        settings_button_frame = tk.Frame(self.settings_frame)
        settings_button_frame.pack(pady=(0,5), padx=10, fill="x")
        tk.Button(settings_button_frame, text="保存设置", command=self.save_settings).pack(side="left", expand=True, fill="x")

    def create_status_bar(self):
        """状态栏"""
        self.status_bar = tk.Frame(self.root, relief=tk.SUNKEN, bd=1)
        self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
        
        self.status_label = tk.Label(self.status_bar, text="就绪", anchor=tk.W)
        self.status_label.pack(side=tk.LEFT)

    def center_window(self):
        self.root.update_idletasks()
        screen_width = self.root.winfo_screenwidth()
        screen_height = self.root.winfo_screenheight()
        
        window_width = 300
        window_height = 400
        
        x = (screen_width - window_width) // 2
        y = (screen_height - window_height) // 2
        self.root.geometry(f"{window_width}x{window_height}+{x}+{y}")

    def toggle_password_visibility(self):
        """切换密码可见性"""
        if self.password_visible:
            # 当前是可见的，切换为隐藏
            self.extract_password_entry.config(show="*")
            self.toggle_password_button.config(text="👁")
            self.password_visible = False
        else:
            # 当前是隐藏的，切换为可见
            self.extract_password_entry.config(show="")
            self.toggle_password_button.config(text="🔒")
            self.password_visible = True    
    
    def toggle_smtp_password_visibility(self):
        """切换SMTP密码可见性"""
        if self.smtp_password_visible:
            self.password_entry.config(show="*")
            self.toggle_smtp_password_button.config(text="👁")
            self.smtp_password_visible = False
        else:
            master_password = self.get_master_password()
            if not master_password:
                messagebox.showerror("错误", "请先设置主密码")
                return
            
            password_window = tk.Toplevel(self.root)
            password_window.title("验证主密码")
            password_window.geometry("300x100")
            password_window.resizable(False, False)
            password_window.transient(self.root)
            password_window.grab_set()
            
            password_window.update_idletasks()
            x = self.root.winfo_x() + (self.root.winfo_width() // 2) - (300 // 2)
            y = self.root.winfo_y() + (self.root.winfo_height() // 2) - (100 // 2)
            password_window.geometry(f"300x100+{x}+{y}")
            
            tk.Label(password_window, text="请输入主密码:").pack(pady=5)
            password_entry = tk.Entry(password_window, show="*", width=30)
            password_entry.pack(pady=5)
            password_entry.focus()
            
            def verify_password():
                input_password = password_entry.get()
                if input_password == master_password:
                    self.password_entry.config(show="")
                    self.toggle_smtp_password_button.config(text="🔒")
                    self.smtp_password_visible = True
                    password_window.destroy()
                else:
                    messagebox.showerror("错误", "主密码不正确，请重新输入")
                    password_entry.delete(0, tk.END)
            
            button_frame = tk.Frame(password_window)
            button_frame.pack(pady=5)
            tk.Button(button_frame, text="确定", command=verify_password).pack(side=tk.LEFT, padx=5)
            tk.Button(button_frame, text="取消", command=password_window.destroy).pack(side=tk.LEFT, padx=5)
            
            password_entry.bind('<Return>', lambda event: verify_password())
            password_window.bind('<Escape>', lambda event: password_window.destroy())
    
    def select_files(self):
        file_path = filedialog.askopenfilename(
            title="选择附件",
            filetypes=[
                ("所有文件", "*.*"),
                ("文本文件", "*.txt"),
                ("PDF文件", "*.pdf"),
                ("Word文档", "*.docx"),
                ("Excel文件", "*.xlsx")
            ]
        )
        
        self.selected_files = [file_path] if file_path else []
        
        self.update_file_info_display()
    
    def update_file_info_display(self):
        self.file_info_text.config(state=tk.NORMAL)
        self.file_info_text.delete(1.0, tk.END)
        
        if not self.selected_files or not self.selected_files[0]:
            self.file_info_text.insert(tk.END, "请拖放文件到此窗口或使用上方的'选择附件'按钮来选择文件\n")
            self.file_info_text.config(state=tk.DISABLED)
            return
        
        file_path = self.selected_files[0]
        file_name = os.path.basename(file_path)
        try:
            file_size = os.path.getsize(file_path)
            file_size_str = self.format_file_size(file_size)
        except Exception as e:
            file_size_str = "无法获取大小"
        
        self.file_info_text.insert(tk.END, f"文件名: {file_name}\n")
        self.file_info_text.insert(tk.END, f"文件大小: {file_size_str}\n")
        self.file_info_text.insert(tk.END, f"路径: {file_path}\n")
        
        self.file_info_text.config(state=tk.DISABLED)
    
    def format_file_size(self, size_bytes):
        """格式化文件大小显示"""
        if size_bytes < 1024:
            return f"{size_bytes} B"
        elif size_bytes < 1024**2:
            return f"{size_bytes/1024:.1f} KB"
        elif size_bytes < 1024**3:
            return f"{size_bytes/(1024**2):.1f} MB"
        else:
            return f"{size_bytes/(1024**3):.1f} GB"

    def derive_key(self, password, salt):
        """从密码派生加密密钥"""
        password_bytes = password.encode('utf-8')
        kdf = PBKDF2HMAC(
            algorithm=hashes.SHA256(),
            length=32,
            salt=salt,
            iterations=100000,
        )
        key = base64.urlsafe_b64encode(kdf.derive(password_bytes))
        return key
    
    def encrypt_data(self, data, password):
        """使用密码加密数据"""
        salt = b'sendtoemail_salt_'  # 固定盐值，实际应用中应使用随机盐值
        key = self.derive_key(password, salt)
        f = Fernet(key)
        encrypted_data = f.encrypt(data.encode('utf-8'))
        return encrypted_data
    
    def decrypt_data(self, encrypted_data, password):
        """使用密码解密数据"""
        salt = b'sendtoemail_salt_'
        key = self.derive_key(password, salt)
        f = Fernet(key)
        try:
            decrypted_data = f.decrypt(encrypted_data)
            return decrypted_data.decode('utf-8')
        except:
            raise Exception("解密失败，请检查主密码是否正确")
    
    def set_master_password(self):
        """设置主密码"""
        master_password = self.master_password_entry.get()
        if not master_password:
            messagebox.showerror("错误", "请输入主密码")
            return
        
        # 获取旧的主密码
        old_master_password = self.get_master_password()
        
        # 如果存在旧的主密码和配置文件，需要重新加密配置文件
        config_data = None
        if old_master_password and os.path.exists("config.json"):
            try:
                with open("config.json", "rb") as f:
                    encrypted_data = f.read()
                
                # 使用旧密码解密配置
                decrypted_data = self.decrypt_data(encrypted_data, old_master_password)
                config_data = json.loads(decrypted_data)
            except Exception as e:
                messagebox.showwarning("警告", f"无法读取现有配置文件: {e}\n将创建新的配置文件")
                config_data = None
        
        # 保存主密码
        try:
            with open("master.key", "w") as f:
                f.write(master_password)
            
            # 如果有配置数据，使用新密码重新加密保存
            if config_data is not None:
                try:
                    config_json = json.dumps(config_data, ensure_ascii=False, indent=4)
                    encrypted_data = self.encrypt_data(config_json, master_password)
                    
                    with open("config.json", "wb") as f:
                        f.write(encrypted_data)
                    messagebox.showinfo("成功", "主密码已设置并更新配置文件")
                except Exception as e:
                    messagebox.showwarning("警告", f"配置文件重新加密失败: {e}")
                    # 即使重新加密失败，也要保存主密码，因为用户明确要求设置新密码
                    messagebox.showinfo("成功", "主密码已设置，但配置文件未更新")
            else:
                messagebox.showinfo("成功", "主密码已设置")
        except Exception as e:
            messagebox.showerror("错误", f"设置主密码失败: {e}")
    
    def get_master_password(self):
        """获取主密码"""
        try:
            with open("master.key", "r") as f:
                return f.read().strip()
        except FileNotFoundError:
            return None
        except Exception as e:
            print(f"获取主密码失败: {e}")
            return None
    
    def on_drop(self, files):
        """处理文件拖放事件"""
        try:
            # windnd库传递的是bytes列表或字符串列表，需要适当地处理
            file_paths = []
            for f in files:
                if isinstance(f, bytes):
                    # 如果是bytes类型，需要解码
                    try:
                        # 首先尝试使用gbk解码
                        file_paths.append(f.decode('gbk'))
                    except UnicodeDecodeError:
                        try:
                        # 如果gbk失败，尝试使用utf-8解码
                            file_paths.append(f.decode('utf-8'))
                        except UnicodeDecodeError:
                            # 如果都失败了，尝试使用系统默认编码
                            file_paths.append(f.decode())
                elif isinstance(f, str):
                    # 如果已经是字符串类型，直接添加
                    file_paths.append(f)
                else:
                    # 其他类型尝试转换为字符串
                    file_paths.append(str(f))
            
            if file_paths:
                # 只取第一个文件，因为我们只支持单文件发送
                file_path = file_paths[0]
                if os.path.isfile(file_path) or os.path.isdir(file_path):
                    self.selected_files = [file_path]
                    self.update_file_info_display()
                    file_type = "文件夹" if os.path.isdir(file_path) else "文件"
                    self.status_label.config(text=f"已拖入{file_type}: {os.path.basename(file_path)}")
                else:
                    messagebox.showerror("错误", "请选择一个有效的文件或文件夹")
        except Exception as e:
            # 出现任何异常都显示错误信息而不是让程序崩溃
            error_msg = f"处理拖放文件时出错: {str(e)}"
            print(error_msg)
            messagebox.showerror("错误", error_msg)

    def test_smtp_connection(self):
        """测试SMTP连接"""
        # 获取SMTP设置
        smtp_server = self.smtp_server_entry.get()
        smtp_port = self.smtp_port_entry.get()
        sender_email = self.sender_email_entry.get()
        password = self.password_entry.get()
        
        # 检查必要字段
        if not all([smtp_server, smtp_port, sender_email, password]):
            messagebox.showerror("错误", "请填写所有SMTP设置项")
            return
            
        try:
            smtp_port = int(smtp_port)
        except ValueError:
            messagebox.showerror("错误", "端口必须是数字")
            return
        
        # 在新线程中测试连接，避免阻塞GUI
        test_thread = threading.Thread(target=self._test_smtp_connection_thread, 
                                     args=(smtp_server, smtp_port, sender_email, password))
        test_thread.daemon = True
        test_thread.start()
    
    def _test_smtp_connection_thread(self, smtp_server, smtp_port, sender_email, password):
        """在后台线程中测试SMTP连接"""
        try:
            # 更新状态栏
            self.status_label.config(text="正在测试SMTP连接...")
            self.root.update()
            
            # 连接SMTP服务器
            server = smtplib.SMTP(smtp_server, smtp_port)
            server.starttls()  # 启用TLS加密
            server.login(sender_email, password)
            server.quit()
            
            # 测试成功
            self.status_label.config(text="SMTP连接测试成功")
            self.root.after(0, lambda: messagebox.showinfo("成功", "SMTP连接测试成功！"))
            
        except smtplib.SMTPAuthenticationError:
            self.status_label.config(text="SMTP认证失败")
            self.root.after(0, lambda: messagebox.showerror("测试失败", "SMTP认证失败，请检查邮箱和密码/授权码"))
        except smtplib.SMTPConnectError:
            self.status_label.config(text="SMTP连接失败")
            self.root.after(0, lambda: messagebox.showerror("测试失败", "SMTP连接失败，请检查服务器和端口设置"))
        except Exception as e:
            self.status_label.config(text="测试失败")
            error_msg = str(e)
            self.root.after(0, lambda: messagebox.showerror("测试失败", f"测试过程中出现错误:\n{error_msg}"))

    def save_settings(self):
        """保存设置的函数"""
        smtp_server = self.smtp_server_entry.get()
        smtp_port = self.smtp_port_entry.get()
        sender_email = self.sender_email_entry.get()
        password = self.password_entry.get()
        receiver_email = self.email_entry.get()
        volume_size = self.volume_size_entry.get()
        extract_password = self.extract_password_entry.get()
        
        # 创建配置字典
        config = {
            "smtp_server": smtp_server,
            "smtp_port": smtp_port,
            "sender_email": sender_email,
            "password": password,
            "receiver_email": receiver_email,
            "volume_size": volume_size,
            "extract_password": extract_password
        }
        
        # 获取主密码
        master_password = self.get_master_password()
        if not master_password:
            master_password = self.master_password_entry.get()
            if not master_password:
                messagebox.showerror("错误", "请先设置主密码")
                return
        
        # 保存到加密的JSON文件
        try:
            config_json = json.dumps(config, ensure_ascii=False, indent=4)
            encrypted_data = self.encrypt_data(config_json, master_password)
            
            with open("config.json", "wb") as f:
                f.write(encrypted_data)
            
            # 更新状态栏
            self.status_label.config(text="设置已保存")
        except Exception as e:
            self.status_label.config(text="保存失败")
            messagebox.showerror("保存失败", f"保存设置时出错: {e}")
    
    def load_settings(self):
        """加载已保存的设置"""
        # 获取主密码
        master_password = self.get_master_password()
        if master_password:
            # 如果存在已保存的主密码，则在界面中显示
            self.master_password_entry.delete(0, tk.END)
            self.master_password_entry.insert(0, master_password)
        
        if not master_password:
            master_password = self.master_password_entry.get()
            if not master_password:
                self.status_label.config(text="请输入主密码以加载设置")
                return
        
        try:
            with open("config.json", "rb") as f:
                encrypted_data = f.read()
            
            # 解密数据
            decrypted_data = self.decrypt_data(encrypted_data, master_password)
            config = json.loads(decrypted_data)
            
            # 填充设置到界面
            self.smtp_server_entry.delete(0, tk.END)
            self.smtp_server_entry.insert(0, config.get("smtp_server", ""))
            
            self.smtp_port_entry.delete(0, tk.END)
            self.smtp_port_entry.insert(0, config.get("smtp_port", ""))
            
            self.sender_email_entry.delete(0, tk.END)
            self.sender_email_entry.insert(0, config.get("sender_email", ""))
            
            # 填充邮件发送相关设置
            self.email_entry.delete(0, tk.END)
            self.email_entry.insert(0, config.get("receiver_email", ""))
            
            self.volume_size_entry.delete(0, tk.END)
            self.volume_size_entry.insert(0, config.get("volume_size", "20"))
                       
            self.extract_password_entry.delete(0, tk.END)
            self.extract_password_entry.insert(0, config.get("extract_password", ""))
            
            # 密码字段
            self.password_entry.delete(0, tk.END)
            self.password_entry.insert(0, config.get("password", ""))
            
            self.status_label.config(text="设置已加载")
        except FileNotFoundError:
            # 配置文件不存在，使用默认值
            self.status_label.config(text="使用默认设置")
        except Exception as e:
            self.status_label.config(text="加载设置失败")
            messagebox.showerror("加载失败", f"加载设置时出错: {e}\n请检查主密码是否正确")
            
    def send_email(self):
        """发送邮件的函数"""
        # 保存当前配置
        self.save_settings()

        # 获取用户输入
        receiver_email = self.email_entry.get()
        volume_size = self.volume_size_entry.get()
        extract_password = self.extract_password_entry.get()
        
        # 检查是否选择了文件
        if not self.selected_files or not self.selected_files[0]:
            messagebox.showerror("错误", "请选择要发送的文件")
            return
            
        # 检查必要字段
        if not receiver_email:
            messagebox.showerror("错误", "请输入接收邮箱")
            return
            
        try:
            volume_size_mb = int(volume_size)
            if volume_size_mb <= 0:
                raise ValueError("分卷大小必须大于0")
        except ValueError as e:
            messagebox.showerror("错误", "分卷大小必须是正整数")
            return
        
        # 禁用发送按钮并更改文本
        self.send_button.config(state="disabled", text="发送中...")
        
        # 在新线程中执行发送操作，避免阻塞GUI
        send_thread = threading.Thread(target=self._send_email_thread, 
                                      args=(receiver_email, volume_size_mb, extract_password))
        send_thread.daemon = True
        send_thread.start()
    
    def _send_email_thread(self, receiver_email, volume_size_mb, extract_password):
        """在后台线程中发送邮件"""
        try:
            # 检查是否有选中的文件
            if not self.selected_files or not self.selected_files[0]:
                raise Exception("没有选中的文件")
            
            # 获取选中的文件
            file_path = self.selected_files[0]
            
            # 显示处理状态
            self.status_label.config(text=f"正在处理文件: {os.path.basename(file_path)}")
            self.root.update()
            
            # 分卷压缩文件
            volume_paths = self.split_and_compress_file(file_path, volume_size_mb, extract_password)
            
            # 发送邮件
            self.status_label.config(text=f"共有{len(volume_paths)}个分卷待发送")
            self.root.update()
            
            for i, volume_path in enumerate(volume_paths):
                self.status_label.config(text=f"正在发送第{i+1}个分卷...")
                self.root.update()
                
                # 发送单个分卷
                self.send_single_email(receiver_email, volume_path, i+1, len(volume_paths), extract_password)
                
                # 如果不是最后一个分卷，则等待随机1-5秒
                if i < len(volume_paths) - 1:
                    import random
                    random_interval = random.randint(1, 5)
                    self.status_label.config(text=f"等待{random_interval}秒后发送下一个分卷...")
                    self.root.update()
                    time.sleep(random_interval)
            
            # 如果有密码，在所有分卷发送完成后再发送密码邮件
            if extract_password:
                self.status_label.config(text="正在发送解压密码...")
                self.root.update()
                self.send_password_email(receiver_email, extract_password)
            
            # 清理临时文件
            for volume_path in volume_paths:
                if os.path.exists(volume_path):
                    os.remove(volume_path)
            
            self.status_label.config(text="发送完成")
            # 恢复发送按钮状态
            self.root.after(0, lambda: self.send_button.config(state="normal", text="发送"))
            self.root.after(0, lambda: messagebox.showinfo("成功", "所有分卷已发送完成"))
            
        except Exception as e:
            self.status_label.config(text="发送失败")
            error_msg = str(e)
            # 恢复发送按钮状态
            self.root.after(0, lambda: self.send_button.config(state="normal", text="发送"))
            self.root.after(0, lambda: messagebox.showerror("发送失败", f"发送过程中出现错误:\n{error_msg}"))
            print(f"发送邮件时出错: {e}")
    
    def split_and_compress_file(self, file_path, volume_size_mb, password=None):
        """分卷压缩文件"""
        volume_size_bytes = volume_size_mb * 1024 * 1024  # 转换为字节
        file_size = os.path.getsize(file_path)
        
        # 如果文件大小小于分卷大小，则直接压缩整个文件
        if file_size <= volume_size_bytes:
            return [self.compress_single_file(file_path, password)]
        
        # 否则使用7-Zip进行分卷压缩
        return self.split_and_compress_with_7zip(file_path, volume_size_mb, password)
        
    def compress_single_file(self, file_path, password=None):
        """使用7-Zip压缩单个文件"""
        file_name = os.path.basename(file_path)
        temp_dir = tempfile.gettempdir()
        archive_filename = file_name + ".7z"
        archive_path = os.path.join(temp_dir, archive_filename)
        
        # 使用py7zr创建加密压缩文件
        with py7zr.SevenZipFile(archive_path, 'w', password=password) as archive:
            archive.write(file_path, file_name)
        
        return archive_path
    
    def split_and_compress_with_7zip(self, file_path, volume_size_mb, password=None):
        """使用7-Zip进行分卷压缩"""
        file_name = os.path.basename(file_path)
        temp_dir = tempfile.gettempdir()
        base_name, ext = os.path.splitext(file_name)
        archive_filename = f"{base_name}.7z"
        archive_path = os.path.join(temp_dir, archive_filename)
        volume_size_bytes = volume_size_mb * 1024 * 1024  # 转换为字节
        
        # 使用py7zr和multivolumefile创建分卷压缩文件
        try:
            with multivolumefile.open(archive_path, mode='wb', volume=volume_size_bytes) as target_archive:
                with py7zr.SevenZipFile(target_archive, 'w', password=password) as archive:
                    archive.write(file_path, file_name)
        except Exception as e:
            raise Exception(f"创建分卷压缩文件时出错: {str(e)}")
        
        # 获取生成的分卷文件列表
        volumes = []
        
        # 检查所有可能的分卷文件格式
        # 首先检查 4 位数字格式 (.0001, .0002, ...)
        part_num = 1
        while True:
            volume_path = os.path.join(temp_dir, f"{archive_filename}.{part_num:04d}")
            if os.path.exists(volume_path):
                volumes.append(volume_path)
                part_num += 1
            else:
                break
        
        # 如果没有找到 4 位数字格式，尝试 3 位数字格式 (.001, .002, ...)
        if not volumes:
            part_num = 1
            while True:
                volume_path = os.path.join(temp_dir, f"{archive_filename}.{part_num:03d}")
                if os.path.exists(volume_path):
                    volumes.append(volume_path)
                    part_num += 1
                else:
                    break
        
        # 如果仍然没有找到分卷文件，检查是否只有一个未分割的文件
        if not volumes and os.path.exists(archive_path):
            volumes.append(archive_path)
            
        if not volumes:
            # 列出临时目录中的所有相关文件以帮助调试
            temp_files = []
            for f in os.listdir(temp_dir):
                if base_name in f:
                    temp_files.append(f)
            
            raise Exception(f"未能生成分卷压缩文件。临时目录中相关文件: {temp_files}")
            
        return volumes
    
    def load_smtp_config(self):
        """加载SMTP配置"""
        try:
            with open("config.json", "rb") as f:
                encrypted_data = f.read()
            
            master_password = self.get_master_password()
            if not master_password:
                master_password = self.master_password_entry.get()
            
            decrypted_data = self.decrypt_data(encrypted_data, master_password)
            config = json.loads(decrypted_data)
            
            smtp_server = config.get("smtp_server")
            smtp_port = int(config.get("smtp_port", 587))
            sender_email = config.get("sender_email")
            sender_password = config.get("password")
            
            if not all([smtp_server, smtp_port, sender_email, sender_password]):
                raise Exception("SMTP设置不完整，请检查设置")
                
            return smtp_server, smtp_port, sender_email, sender_password
        except Exception as e:
            raise Exception(f"无法加载配置文件: {e}")
    def send_single_email(self, receiver_email, attachment_path, part_num, total_parts, password=None):
        """发送单个邮件"""
        # 加载SMTP设置
        smtp_server, smtp_port, sender_email, sender_password = self.load_smtp_config()
        
        # 获取文件名作为邮件主题的一部分
        file_name = os.path.basename(attachment_path).split('.')[0]
        
        # 创建邮件
        msg = MIMEMultipart()
        msg['From'] = sender_email
        msg['To'] = receiver_email
        msg['Subject'] = f"{file_name} - 文件分卷 {part_num}/{total_parts}"
        
        # 添加邮件正文
        body = f"这是文件的第 {part_num} 个分卷，共 {total_parts} 个分卷。"
        msg.attach(MIMEText(body, 'plain', 'utf-8'))
        
        # 添加附件
        with open(attachment_path, "rb") as attachment:
            part = MIMEBase('application', 'octet-stream')
            part.set_payload(attachment.read())
        
        encoders.encode_base64(part)
        filename = os.path.basename(attachment_path)
        from email.header import Header
        part.add_header(
            'Content-Disposition',
            'attachment', 
            filename=Header(filename, 'utf-8').encode()
        )
        part.add_header(
            'Content-Type',
            'application/octet-stream'
        )
        msg.attach(part)
        
        # 发送邮件
        try:
            server = smtplib.SMTP(smtp_server, smtp_port)
            server.starttls()
            server.login(sender_email, sender_password)
            server.sendmail(sender_email, receiver_email, msg.as_bytes())
            server.quit()
        except Exception as e:
            raise Exception(f"发送邮件失败: {e}")
    
    def send_password_email(self, receiver_email, password):
        """发送包含解压密码的邮件"""
        # 加载SMTP设置
        smtp_server, smtp_port, sender_email, sender_password = self.load_smtp_config()
        
        # 创建邮件
        msg = MIMEMultipart()
        msg['From'] = sender_email
        msg['To'] = receiver_email
        msg['Subject'] = "文件解压密码"
        
        # 添加邮件正文
        body = f"您收到的文件分卷的解压密码是: {password}\n\n请妥善保管此密码，仅用于解压文件。"
        msg.attach(MIMEText(body, 'plain', 'utf-8'))
        
        # 发送邮件
        try:
            server = smtplib.SMTP(smtp_server, smtp_port)
            server.starttls()
            server.login(sender_email, sender_password)
            server.sendmail(sender_email, receiver_email, msg.as_string())
            server.quit()
        except Exception as e:
            raise Exception(f"发送密码邮件失败: {e}")
        
if __name__ == "__main__":
    app = sendtoABCmail()
    app.root.mainloop()